import { Equal, Every, Some } from "../helpers";
import { Compose, Fn, PartialApply, unset } from "../core/Core";

export namespace Booleans {
  type ExtendsImpl<a, b> = [a] extends [b] ? true : false;

  interface ExtendsFn extends Fn {
    return: this["args"] extends [infer first, infer second, ...any]
      ? ExtendsImpl<first, second>
      : never;
  }

  export type Extends<a = unset, b = unset> = PartialApply<
    ExtendsFn,
    b extends unset ? [unset, a] : [a, b]
  >;

  type NotImpl<a> = a extends true ? false : true;

  interface NotFn extends Fn {
    return: this["args"] extends [infer first, ...any] ? NotImpl<first> : never;
  }

  export type Not<a = unset> = PartialApply<NotFn, [a]>;

  interface EqualsFn extends Fn {
    return: this["args"] extends [infer a, infer b, ...any]
      ? Equal<a, b>
      : never;
  }

  export type Equals<a = unset, b = unset> = PartialApply<EqualsFn, [a, b]>;

  export type NotEqual<a = unset, b = unset> = Compose<
    [Not, PartialApply<EqualsFn, [a, b]>]
  >;

  export type DoesNotExtend<a = unset, b = unset> = Compose<
    [Not, PartialApply<ExtendsFn, [a, b]>]
  >;

  interface AndFn extends Fn {
    return: this["args"] extends [
      infer first extends boolean,
      infer second extends boolean,
      ...any
    ]
      ? Every<[first, second]>
      : never;
  }

  export type And<a = unset, b = unset> = PartialApply<AndFn, [a, b]>;

  interface OrFn extends Fn {
    return: this["args"] extends [
      infer first extends boolean,
      infer second extends boolean,
      ...any
    ]
      ? Some<[first, second]>
      : never;
  }

  export type Or<a = unset, b = unset> = PartialApply<OrFn, [a, b]>;

  interface XOrFn extends Fn {
    return: this["args"] extends [
      infer first extends boolean,
      infer second extends boolean,
      ...any
    ]
      ? first extends second
        ? false
        : true
      : never;
  }

  export type XOr<a = unset, b = unset> = PartialApply<XOrFn, [a, b]>;
}
